/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ohosadvance.topsnackbar;

import com.ohosadvance.topsnackbar.utils.PixelUtil;

import ohos.agp.animation.Animator;
import ohos.agp.components.Component;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Text;
import ohos.agp.components.Button;
import ohos.agp.components.AttrSet;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;

import ohos.app.Context;

import ohos.multimodalinput.event.TouchEvent;

/**
 * description :
 *
 * @since 2021/7/2
 */
public class SnackbarLayout extends DirectionalLayout implements Component.EstimateSizeListener,
        Component.BindStateChangedListener, Component.TouchEventListener {
    private Text mMessageView;
    private Button mActionView;
    private OnAttachStateChangeListener mOnAttachStateChangeListener;
    private OnToucheStateChangeListener onToucheStateChangeListener;
    private double downX;
    private long startTime;
    private boolean isBeingDragged;
    int mMaxWidth;
    final int mMaxInlineActionWidth;

    /**
     * 创建 SnackbarLayout
     *
     * @param context context
     */
    public SnackbarLayout(Context context) {
        this(context, null);
    }

    /**
     * 创建 SnackbarLayout
     *
     * @param context context
     * @param attrSet attrs
     */
    public SnackbarLayout(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    /**
     * 创建 SnackbarLayout
     *
     * @param context context
     * @param attrs attrs
     * @param styleName styleName
     */
    public SnackbarLayout(Context context, AttrSet attrs, String styleName) {
        super(context, attrs, styleName);
        setClickable(true);
        if (attrs != null && attrs.getAttr("max_width").isPresent()) {
            mMaxWidth = attrs.getAttr("max_width").get().getDimensionValue();
        } else {
            mMaxWidth = -1;
        }
        if (attrs != null && attrs.getAttr("maxActionInlineWidth").isPresent()) {
            mMaxInlineActionWidth = attrs.getAttr("maxActionInlineWidth").get().getIntegerValue();
        } else {
            mMaxInlineActionWidth = -1;
        }

        setTouchEventListener(this);
        setEstimateSizeListener(this);
    }

    public boolean isBeingDragged() {
        return isBeingDragged;
    }

    /**
     * set OnToucheStateChangeListener
     *
     * @param onToucheStateChangeListener onToucheStateChangeListener
     */
    public void setOnToucheStateChangeListener(OnToucheStateChangeListener onToucheStateChangeListener) {
        this.onToucheStateChangeListener = onToucheStateChangeListener;
    }

    void setMessageView(Text messageView) {
        mMessageView = messageView;
    }

    Text getMessageView() {
        return mMessageView;
    }

    void setActionView(Button actionView) {
        mActionView = actionView;
    }

    Button getActionView() {
        return mActionView;
    }

    @Override
    public boolean onEstimateSize(int widthEstimatedConfig, int heightEstimatedConfig) {
        int width = Component.EstimateSpec.getSize(widthEstimatedConfig);
        int height = Component.EstimateSpec.getSize(heightEstimatedConfig);
        if (mMaxWidth > 0 && width > mMaxWidth) {
            setEstimatedSize(
                    Component.EstimateSpec.
                            getChildSizeWithMode(mMaxWidth, mMaxWidth, EstimateSpec.NOT_EXCEED),
                    Component.EstimateSpec.
                            getChildSizeWithMode(height, heightEstimatedConfig, EstimateSpec.NOT_EXCEED));
        }
        final int multiLineVPadding = (int) PixelUtil.vp2px(24);
        final int singleLineVPadding = (int) PixelUtil.vp2px(14);
        boolean remeasure = false;
        if (mMessageView.isMultipleLine() && mMaxInlineActionWidth > 0
                && mActionView.getEstimatedWidth() > mMaxInlineActionWidth) {
            if (updateViewsWithinLayout(VERTICAL, multiLineVPadding,
                    multiLineVPadding - singleLineVPadding)) {
                remeasure = true;
            }
        } else {
            if (updateViewsWithinLayout(HORIZONTAL, singleLineVPadding, singleLineVPadding)) {
                remeasure = true;
            }
        }
        if (remeasure) {
            setEstimatedSize(
                    Component.EstimateSpec.getChildSizeWithMode(width, width, Component.EstimateSpec.NOT_EXCEED),
                    Component.EstimateSpec.getChildSizeWithMode(height, height, Component.EstimateSpec.NOT_EXCEED));
        }
        return false;
    }

    /**
     * in ChildComponent animate
     *
     * @param delay delay
     * @param duration duration
     */
    void animateChildrenIn(int delay, int duration) {
        mMessageView.setAlpha(0F);
        mMessageView.createAnimatorProperty()
                .alpha(1F)
                .setDuration(duration)
                .setDelay(delay)
                .start();
        if (mActionView.getVisibility() == VISIBLE) {
            mActionView.setAlpha(0F);
            mActionView.createAnimatorProperty()
                    .alpha(1F)
                    .setDuration(duration)
                    .setDelay(delay)
                    .start();
        }
    }

    /**
     * out ChildComponent animate
     *
     * @param delay delay
     * @param duration duration
     */
    void animateChildrenOut(int delay, int duration) {
        mMessageView.setAlpha(1F);
        mMessageView.createAnimatorProperty()
                .alpha(0F)
                .setDuration(duration)
                .setDelay(delay)
                .start();
        if (mActionView.getVisibility() == VISIBLE) {
            mActionView.setAlpha(1F);
            mActionView.createAnimatorProperty()
                    .alpha(0F)
                    .setDuration(duration)
                    .setDelay(delay)
                    .start();
        }
    }

    @Override
    public boolean isBoundToWindow() {
        return super.isBoundToWindow();
    }

    @Override
    public void onComponentBoundToWindow(Component component) {
        if (mOnAttachStateChangeListener != null) {
            mOnAttachStateChangeListener.onViewAttachedToWindow(component);
        }
    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {
        if (mOnAttachStateChangeListener != null) {
            mOnAttachStateChangeListener.onViewDetachedFromWindow(this);
        }
    }

    void setOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
        mOnAttachStateChangeListener = listener;
    }

    private boolean updateViewsWithinLayout(final int orientation,
                                                final int messagePadTop, final int messagePadBottom) {
        boolean changed = false;
        if (orientation != getOrientation()) {
            setOrientation(orientation);
            changed = true;
        }
        if (mMessageView.getPaddingTop() != messagePadTop
                || mMessageView.getPaddingBottom() != messagePadBottom) {
            updateTopBottomPadding(mMessageView, messagePadTop, messagePadBottom);
            changed = true;
        }
        return changed;
    }

    private static void updateTopBottomPadding(Component view, int topPadding, int bottomPadding) {
        view.setPadding(view.getPaddingLeft(), topPadding,
                view.getPaddingRight(), bottomPadding);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                downX = touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
                startTime = touchEvent.getOccurredTime();

                /* fall-through */
            case TouchEvent.POINT_MOVE:
                isBeingDragged = true;
                if (onToucheStateChangeListener != null) {
                    onToucheStateChangeListener.onActionDownAndMove();
                }

                    break;
            case TouchEvent.PRIMARY_POINT_UP:
                double upX;
                long endTime;
                isBeingDragged = false;
                if (onToucheStateChangeListener != null) {
                    onToucheStateChangeListener.onActionUp();
                }
                upX = touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
                if (upX < downX) {
                    break;
                }
                endTime = touchEvent.getOccurredTime();
                DisplayAttributes attributes = DisplayManager.getInstance().getDefaultDisplay(mContext)
                        .get().getAttributes();
                double density = ((double)attributes.xDpi + (double)attributes.yDpi) / 2f;
                double velocity = ((1000F * (upX - downX)) / (((double)endTime - (double) startTime) * density));
                if (velocity > 2) {
                    swipingDismiss();
                }
                        break;
        }
        return true;
    }

    private void swipingDismiss() {
        int width = DisplayManager.getInstance().getDefaultDisplay(mContext).get().getAttributes().width;
        createAnimatorProperty()
                .moveFromX(0)
                .moveToX(width)
                .setDuration(150)
                .setStateChangedListener(new Animator.StateChangedListener() {
                    @Override
                    public void onStart(Animator animator) {}

                    @Override
                    public void onStop(Animator animator) {}

                    @Override
                    public void onCancel(Animator animator) {}

                    @Override
                    public void onEnd(Animator animator) {
                        if (onToucheStateChangeListener != null) {
                            onToucheStateChangeListener.onShouldDismiss();
                        }
                    }

                    @Override
                    public void onPause(Animator animator) {}

                    @Override
                    public void onResume(Animator animator) {}
                })
                .start();
    }

    interface OnAttachStateChangeListener {
        void onViewAttachedToWindow(Component component);

        void onViewDetachedFromWindow(Component component);
    }

    /**
     * On snackbarLayout toucheStateChangeListener
     */
    public interface OnToucheStateChangeListener {
        void onActionDownAndMove();

        void onActionUp();

        void onShouldDismiss();
    }
}
